/** json字符串解析类
 * @file jsonparse.hpp
 * @author Kiven Lee
 * @version 1.0
 * @date 2021-09-10
 *
 * @copyright Kivensoft (c) 2018 - 2021
 *
 */
#pragma once

#include <cstddef>
#include <cinttypes>
#include <ctime>
#include <string>
#include <string_view>
#include <optional>
#include <tuple>
#include <functional>
#include "mjson.h"
#include "utils.hpp"

namespace klib {
    class json_parse_t {

    private:
        const char* _data;
        size_t      _size;

        const char* _get(const char* key) {
            const char *vptr;
            int vlen, vtype = mjson_find(_data, _size, key, &vptr, &vlen);
            return vtype == MJSON_TOK_INVALID ? nullptr : *vptr == '"' ? vptr + 1 : vptr;
        }

    public:
        /** 空的string_view全局常量 */
        static inline constexpr std::string_view empty_sv = std::string_view();

        /** json值枚举值 */
        enum class value_type { error, vnull, vbool, vnumber, vstring, varray, vobject };

    public:
        json_parse_t(const char* data, size_t size): _data(data), _size(size) {}
        json_parse_t(const std::string_view& data): _data(data.data()), _size(data.length()) {}

        json_parse_t(const json_parse_t& src): _data(src._data), _size(src._size) {}
        json_parse_t& operator =(const json_parse_t& src) {
            _data = src._data;
            _size = src._size;
            return *this;
        }

        const char* data() const noexcept { return _data; }
        size_t length() const noexcept { return _size; }

        std::tuple<std::string_view, value_type> get(const char* key) {
            const char *vptr; int vlen;
            int vtype = mjson_find(_data, _size, key, &vptr, &vlen);

            if (vtype == MJSON_TOK_INVALID) return std::make_tuple(empty_sv, value_type::error);
            if (*vptr == '"') { ++vptr; vlen -= 2; }

            value_type vt;
            switch (vtype) {
                case MJSON_TOK_STRING: vt = value_type::vstring; break;
                case MJSON_TOK_NUMBER: vt = value_type::vnumber; break;
                case MJSON_TOK_TRUE:
                case MJSON_TOK_FALSE: vt = value_type::vbool; break;
                case MJSON_TOK_NULL: vt = value_type::vnull; break;
                case MJSON_TOK_ARRAY: vt = value_type::varray; break;
                case MJSON_TOK_OBJECT: vt = value_type::vobject; break;
            }

            return make_tuple(std::string_view(vptr, vlen), vt);
        }

        std::optional<bool> get_bool(const char* key) {
            // return get_bool(_data, _size, key);
            const char* vptr = _get(key);
            // 使用快速判定法，只比较第一个字符,"t"表示true,'f'表示false，其它表示null
            if (vptr) {
                switch (*vptr) {
                    case 't': return std::optional(true);
                    case 'f': return std::optional(false);
                }
            }
            return std::nullopt;
        }

        bool get_bool(const char* key, bool def) {
            auto v = get_bool(key);
            return v.has_value() ? v.value() : def;
        }

        std::optional<int64_t> get_int64(const char* key) {
            auto v = get_double(key);
            return v.has_value() ? std::optional(int64_t(v.value())) : std::nullopt;
        }

        int64_t get_int64(const char* key, int64_t def) {
            auto v = get_int64(key);
            return v.has_value() ? v.value() : def;
        }

        std::optional<double> get_double(const char* key) {
            const char* vptr = _get(key);
            double r;
            return vptr && sscanf(vptr, "%lf", &r) != -1 ? std::optional(r) : std::nullopt;
        }

        double get_double(const char* key, double def) {
            auto v = get_double(key);
            return v.has_value() ? v.value() : def;
        }

        std::optional<std::time_t> get_time(const char* key) {
            const char* vptr = _get(key);
            if (vptr) {
                std::tm t;
                if (std::sscanf(vptr, "%d-%d-%d %d:%d:%d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec) != -1) {
                    --t.tm_mon;
                    return std::optional(std::mktime(&t));
                }
            }
            return std::nullopt;
        }

        std::time_t get_time(const char* key, std::time_t def) {
            auto v = get_time(key);
            return v.has_value() ? v.value() : def;
        }

        std::optional<std::string_view> get_string(const char* key) {
            auto [v, t] = get(key);
            return t != value_type::vstring ? std::nullopt : std::optional(v);
        }

        std::string_view get_string(const char* key, const std::string_view& def) {
            auto [v, t] = get(key);
            return t != value_type::vstring ? def : v;
        }

        std::optional<std::string_view> get_object(const char* key) {
            auto [v, t] = get(key);
            return t != value_type::vobject ? std::nullopt : std::optional(v);
        }

        std::optional<std::string_view> get_array(const char* key) {
            auto [v, t] = get(key);
            return t != value_type::varray ? std::nullopt : std::optional(v);
        }

        /** 获取已解析的字符串值
         *
         * @param key 键名
         * @param alloc_fn 回调函数，由用户自行分配内存并返回给调用函数
         * @return size_t 实际写入字节数
         */
        size_t get_parse_string(const char* key, const std::function<char* (size_t len)>& alloc_fn) {
            size_t count = 0;
            auto r = get_string(key);
            if (r.has_value()) {
                char* buf = alloc_fn(r.value().length());
                if (buf) count = jstirng_conv(buf, r.value().data(), r.value().length());
            }
            return count;
        }

        template<size_t _Num>
        bool get_parse_string(const char* key, dstring_t<_Num>& s) {
            auto r = get_string(key);
            if (r.has_value()) {
                s.set_length(r.value().length());
                s.set_length(jstirng_conv(s.data(), r.value().data(), r.value().length()));
                return true;
            }
            return false;
        }

        template<size_t _Num>
        bool get_parse_string(const char* key, dstring_t<_Num>& s, const std::string_view& def) {
            bool b = get_parse_string(key, s);
            if (!b || !s.length()) s.write(def.data(), def.length());
            return b;
        }

        /** json格式的字符串转换成c字符串
         *
         * @param dst 要写入的目标内存地址, dst为nullptr时，只计算需要的长度
         * @param src 转换源地址
         * @param size 源长度
         * @return size_t 目标写入长度
         */
        static inline size_t jstirng_conv(char* dst, const char* src, size_t size) {
            if (dst) {
                char *p = dst;
                for (const char *se = src + size; src < se; ++src, ++p) {
                    char c = *src;
                    if (c != '\\') *p = c;
                    else if (++src < se) {
                        char c2 = *src;
                        switch (c2) {
                            case 'b': *p = '\b'; break;
                            case 't': *p = '\t'; break;
                            case 'n': *p = '\n'; break;
                            case 'f': *p = '\f'; break;
                            case 'r': *p = '\r'; break;
                            default: *p = c2; break;
                        }
                    }
                }
                return p - dst;
            } else {
                size_t count = 0;
                for (const char *se = src + size; src < se; ++src, ++count)
                    if (*src == '\\' && ++src >= se) break;
                return count;
            }
        }

        static inline std::string jstring_to_string(const char* src, size_t size) {
            std::string s;
            s.resize(size);
            s.resize(jstirng_conv(s.data(), src, size));
            return s;
        }
    };

} // namespace klib
